/*
 * // +-------------------------------------------------------------------------------------------------
 * // |                 有你就好 [ 有节骨乃坚，无心品自端 ]     <http://encoding.wang>
 * // +-------------------------------------------------------------------------------------------------
 * // |                             独在异乡为异客         每逢佳节倍思亲
 * // +-------------------------------------------------------------------------------------------------
 * // |                 联系:   <707069100@qq.com>      <http://weibo.com/513778937>
 * // +-------------------------------------------------------------------------------------------------
 */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                   ErYang出品 属于小极品          共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------


package wang.encoding.mroot.admin.common.aop


import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import wang.encoding.mroot.admin.common.constant.ConfigConst
import wang.encoding.mroot.admin.common.util.ShiroSessionUtil
import wang.encoding.mroot.common.annotation.RequestLogAnnotation
import wang.encoding.mroot.common.config.LocaleMessageSourceConfiguration
import wang.encoding.mroot.common.exception.BaseException
import wang.encoding.mroot.common.util.HttpRequestUtil
import wang.encoding.mroot.model.entity.system.RequestLog
import wang.encoding.mroot.model.entity.system.User
import wang.encoding.mroot.model.enums.UserTypeEnum
import wang.encoding.mroot.service.event.RequestLogAopEvent
import java.lang.reflect.Method
import java.time.Instant
import java.util.*
import javax.servlet.http.HttpServletRequest


/**
 * 请求日志 aop
 *
 * @author ErYang
 */
@Aspect
@Component
class RequestLogAop {


    companion object {

        private val logger: Logger = LoggerFactory.getLogger(RequestLogAop::class.java)

        private const val USER_AGENT = "User-Agent"

        private const val GET = "GET"
    }


    @Autowired
    private lateinit var httpRequestUtil: HttpRequestUtil

    @Autowired
    protected lateinit var applicationContext: ApplicationContext

    @Autowired
    private lateinit var localeMessageSourceConfiguration: LocaleMessageSourceConfiguration

    @Autowired
    private lateinit var configProperties: ConfigConst


    /**
     * 拦截  wang.encoding.mroot.admin.controller 包下方法存在 WebLogger 注解的方法
     */
    @Pointcut("execution(public * wang.encoding.mroot.*.controller..*(..))" +
            "&& (@annotation(wang.encoding.mroot.common.annotation.RequestLogAnnotation))")
    private fun pointCutMethod() {
    }

    // -------------------------------------------------------------------------------------------------

    /**
     * 保存请求日志
     *
     * @param joinPoint ProceedingJoinPoint
     * @return Object
     */
    @Around(value = "pointCutMethod()")
    fun requestLog(joinPoint: ProceedingJoinPoint): Any? {
        val start: Long = Instant.now().toEpochMilli()
        val returnResult: Any
        try {
            returnResult = joinPoint.proceed()
        } catch (e: RuntimeException) {
//            if (logger.isErrorEnabled) {
//                logger.error(localeMessageSourceConfiguration.getMessage("message.exception.title"), e)
//            }
            throw BaseException(localeMessageSourceConfiguration.getMessage("message.exception.title"), e)
        }

        val target: Any = joinPoint.target
        // 方法名称
        val methodName: String = joinPoint.signature.name

        val parameterTypes: Array<Class<*>> = (joinPoint.signature as MethodSignature).method.parameterTypes
        val method: Method
        // 通过反射获得拦截的 method
        try {
            method = target.javaClass.getMethod(methodName, *parameterTypes)
        } catch (e: NoSuchMethodException) {
            if (logger.isErrorEnabled) {
                logger.error(localeMessageSourceConfiguration.getMessage("message.exception.title"), e)
            }
            return null
        }

        val attributes: ServletRequestAttributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes

        val request: HttpServletRequest = attributes.request

        val requestLogAnnotation: RequestLogAnnotation = method.getAnnotation(RequestLogAnnotation::class.java)
                ?: return returnResult
        // 如果方法上没有注解 返回
        // sessionID
        val sessionName: String = request.session.id
        // 类名
        val className: String = target.javaClass.name
        // 方法类型
        val methodType: String = request.method

        // 请求日志
        val requestLog = RequestLog()
        val user: User? = ShiroSessionUtil.getAttribute(configProperties.adminSessionName) as User?
        if (null != user) {
            requestLog.userId = user.id
            requestLog.username = user.username
            if (null != user.type) {
                requestLog.userType = UserTypeEnum.getValueByKey(user.type!!)
            }
        }
        requestLog.module = localeMessageSourceConfiguration.getMessage(requestLogAnnotation.module, null,
                requestLogAnnotation.module)
        requestLog.title = localeMessageSourceConfiguration.getMessage(requestLogAnnotation.title, null,
                requestLogAnnotation.title)
        requestLog.className = className
        requestLog.methodName = methodName
        requestLog.sessionName = sessionName
        requestLog.url = request.requestURL.toString()
        requestLog.userAgent = request.getHeader(USER_AGENT)
        requestLog.methodType = methodType
        if (GET.equals(methodType, ignoreCase = true)) {
            requestLog.params = request.queryString
        } else {
            // 获取所有请求参数 封装到 Map 中
            val map: Map<String, Array<String>> = request.parameterMap
            requestLog.params = initPostParameter(map)
        }
        requestLog.result = returnResult.toString()
        requestLog.remark = requestLogAnnotation.remark
        requestLog.gmtCreate = Date.from(Instant.now())
        requestLog.gmtCreateIp = httpRequestUtil.getIp(request)
        val end: Long = Instant.now().toEpochMilli()
        requestLog.executeTime = end - start
        // 异步新增数据
        val requestLogAopEvent = RequestLogAopEvent(this, requestLog)
        applicationContext.publishEvent(requestLogAopEvent)
        return returnResult
    }

    // -------------------------------------------------------------------------------------------------

    /**
     * 创建 post 请求参数
     *
     * @param map Map<String></String>, String[]>
     * @return 参数
     */
    private fun initPostParameter(map: Map<String, Array<String>>): String {
        val stringBuilder = StringBuilder("")
        for ((key: String, value: Array<String>) in map) {
            stringBuilder.append("{")
            stringBuilder.append(key)
            stringBuilder.append("=")
            stringBuilder.append(Arrays.toString(value))
            stringBuilder.append("}")
            stringBuilder.append(",")
        }
        if (1 < stringBuilder.length) {
            stringBuilder.deleteCharAt(stringBuilder.toString().length - 1)
        }
        return stringBuilder.toString()
    }

    // -------------------------------------------------------------------------------------------------

}

// -----------------------------------------------------------------------------------------------------

// End RequestLogAop class

/* End of file RequestLogAop.kt */
/* Location: ./src/main/kotlin/wang/encoding/mroot/admin/common/aop/RequestLogAop.kt */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                           ErYang出品 属于小极品  O(∩_∩)O~~   共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------

// -----------------------------------------------------------------------------------------------------
